local oo = require("SharedLibs.BaseLib")

Debugger = require("SharedLibs.Debugger")

IReadOnlyProperty = require("SharedLibs.Signal.IReadOnlyProperty")
ISignal = require("SharedLibs.Signal.ISignal")
Signal = require("SharedLibs.Signal.Signal")

--[[
	把 N 属性组合成一个新属性。
	和TupleProperty类似, 但是TupleProperty组合出来的是元组属性, 任何
	一个子属性变化都会触发changed事件.
	
	而本类组合出的还是普通属性, 只有当组合后的属性值变化时才会触发changed事件.
	
	各种多元Condition操作, 例如OrN, AndN, XOrN都是用该类实现的

	注意: Map后的Property每次GetValue时都要执行一次Map函数. 如果Map的嵌套层次很高, 有可能有性能问题
	如果出现性能问题, 上层可以自行实现cache, 也就是如果parent的值没有变化, 并且parent不是table或Instance.
	就可以不用Map, 直接返回cache的值
]] 
local cls = oo.class();
cls:implements(IReadOnlyProperty);

function cls:constructor(mapper, ...)
	assert(mapper);
	local props = {...};
	assert(#props > 0);
	self.name = oo.GetClassName(cls);
	
	self._changed = Signal.new("PropertyChanged");
	--- Listener count
	self._lisN = 0;
	
	self._props = props;
	--- 用来销毁所有的监听器的数组
	self._destroyers = {}; 
	
	self._mapper = mapper;

	local vals = {};
	for _, p in ipairs(props) do
		table.insert(vals, p:GetValue());
	end
	self._val = mapper(table.unpack(vals));

end

function cls:GetValue()
	local vals = {};
	for _, p in ipairs(self._props) do
		table.insert(vals, p:GetValue());
	end

	local val = self._mapper(table.unpack(vals));
	if val ~= self._val then
		self._val = val;
		self._changed:Emit(val);
	end
	return val;
end

function cls:On(handler, retDestroyer)
	self._lisN = self._lisN + 1;
	self._changed:On(handler);

	if self._lisN == 1 then
		for i, prop in ipairs(self._props) do
			local onPropChanged = function(value)
				local vals = {};
				for j, prop in ipairs(self._props) do
					if j== i then
						table.insert(vals, value);
					else
						table.insert(vals, prop:GetValue());
					end
				end
				local newVal = self._mapper(table.unpack(vals));
				if newVal ~= self._val then
					self._val = newVal;
					self._changed:Emit(newVal);
				end
			end			
			local destroyer = prop:On(onPropChanged);
			table.insert(self._destroyers, destroyer);
		end		
	end

	if retDestroyer then return function() self:Off(handler); end end
end

function cls:Off(handler)
	if self._lisN == 0 then return end;
		
	self._lisN = self._lisN - 1;
	assert(self._lisN >= 0);
	self._changed:Off(handler);

	if self._lisN == 0 then
		for _, destroyer in ipairs(self._destroyers) do
			destroyer();
		end
		self._destroyers = {};
	end
end

function cls:Clear()
	local needOff = self._lisN ~= 0;

	self._lisN = 0;
	self._changed:Clear();

	if needOff then
		for _, destroyer in ipairs(self._destroyers) do
			destroyer();
		end
		self._destroyers = {};
	end
end

return cls;

